package ua.kpi.ipsa.expert.api.adminpanel;

import java.awt.*;
import java.awt.event.*;
import java.rmi.*;
import java.rmi.registry.*;
import java.rmi.server.*;
import java.util.*;
import java.util.List;

import javax.swing.*;
import javax.swing.event.*;

import ua.kpi.ipsa.expert.api.net.*;
import ua.kpi.ipsa.expert.api.server.*;

/**
 * 
 * @author doctorpro
 * 
 */
public class AdminpanelMain extends JFrame implements
		ConnectionApplicationListener {

	//
	public static final int ADMINPANEL_REGISTRY_PORT = 1098;

	//
	private AdminsConsole adminConsole;

	//
	private AdminpanelGui gui;

	//
	private volatile boolean connected = false;
	private volatile boolean serverOnline = false;
	private volatile boolean keepBursting = true;

	//
	private ServerGateway gateway;

	//
	private RemoteUserHandler userHandler;
	private RemoteUserListener userHandlerStub;

	//
	private Thread triar;

	//
	private int BURSTER_SLEEP_TIME = 3000;

	//
	private UsersListModel userListModel;

	/**
	 * 
	 * @param args
	 */
	public static final void main(String[] args) {

		AdminpanelMain main = new AdminpanelMain();
		main.setSize(640, 480);
		main.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
		Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
		main.setLocation((d.width - main.getWidth()) / 2,
				(d.height - main.getHeight()) / 2);
		main.pack();
		main.setVisible(true);
	}

	public AdminpanelMain() {
		super("API adminpanell");

		//
		addWindowListener(new CloseHandler());

		//
		registerRemoteUserHandler();

		//
		putGui();

		//
		launchTriar();
	}

	/**
	 * 
	 */
	private void putGui() {
		gui = new AdminpanelGui(this);
		gui.setUserListModel(userListModel, userListModel);
		setContentPane(gui);
	}

	/**
	 * 
	 * @return
	 */
	private String getServerHost() {
		//return "192.168.0.1"
		return "localhost";
	}

	/**
	 * 
	 */
	private void onConnectedToServer() {
		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				connected = true;
				gui.onConnectedToServer();

				// reload the model
				userListModel.reloadModel(getCurrentUsers());
			}
		});
	}

	private List<RemoteUserImpl> getCurrentUsers() {

		ArrayList<RemoteUserImpl> result = new ArrayList<RemoteUserImpl>();

		try {
			List<String> logins = adminConsole.getUsers();
			for (Iterator<String> i = logins.iterator(); i.hasNext();) {
				RemoteUserImpl u = new RemoteUserImpl(i.next());
				u.loggedIn = (adminConsole.isUserLogged(u.getLogin()));
				if (!u.getLogin().equals(UserConsoleManager.ADMIN_NAME)) {
					result.add(u);
				};
			}
							
		} catch (RemoteException ex) {
			ex.printStackTrace();
		}
		return result;
	}

	/**
	 * 
	 */
	private void onServerOnline() {
		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				serverOnline = true;
				gui.onServerOnline();
			}
		});
	}

	/**
	 * 
	 */
	private void onServerOffline() {
		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				serverOnline = false;
				gui.onServerOffline();
			}
		});

	}

	/**
	 * 
	 */
	private void onServerNoinfo() {
		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				serverOnline = false;
				gui.onServerNoinfo();
			}
		});
	}

	/**
	 * 
	 */
	private void onDisconnectedFromServer() {
		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				connected = false;
				gui.onDisconnectedFromServer();

				// empty
				userListModel.clear();
			}
		});
	}

	/**
	 * 
	 */
	private void launchTriar() {

		triar = new Thread() {
			@Override
			public void run() {
				while (keepBursting) {
					verifyServerState();
					try {
						sleep(BURSTER_SLEEP_TIME);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		};

		triar.start();
	}

	private void verifyServerState() {

		if (!connected) {
			Registry r = null;

			try {
				r = LocateRegistry.getRegistry(getServerHost(),
						ServerMain.SERVER_PORT);

				try {
					gateway = (ServerGateway) r
							.lookup(ServerMain.REMOTE_GATEWAY_NAME);
					onServerOnline();
				} catch (AccessException e) {
					onServerNoinfo();
					e.printStackTrace();
				} catch (RemoteException e) {

					// watch this place!
					if (connected) {
						onDisconnectedFromServer();
					}

					onServerOffline();
					// e.printStackTrace();
				} catch (NotBoundException e) {
					onServerOffline();
					e.printStackTrace();
				}

			} catch (RemoteException ex) {
				onServerOffline();
				ex.printStackTrace();
				return;
			}
		} else {
			try {
				adminConsole.assertOnline();
			} catch (RemoteException e) {
				adminConsole = null;
				onDisconnectedFromServer();
				onServerOffline();
				// e.printStackTrace();
			}
		}
	}

	/**
	 * 
	 */
	private void registerRemoteUserHandler() {

		try {
			System.out.println("Creating regitry on port "
					+ ADMINPANEL_REGISTRY_PORT + " ...");
			LocateRegistry.createRegistry(ADMINPANEL_REGISTRY_PORT);
			System.out.println("Registry created [OK]");

			System.out.println("Registering remote user handler instance ...");
			userListModel = new UsersListModel();
			userHandler = new RemoteUserHandler(userListModel);
			userHandlerStub = (RemoteUserListener) UnicastRemoteObject
					.exportObject(userHandler, ADMINPANEL_REGISTRY_PORT);
			System.out.println("Remote user handler instance exported [OK]");

		} catch (RemoteException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 
	 */
	@Override
	public void applyDisconnection() {

		if (connected) {

			// adminConsole can't be null. if is -> there is bug
			if (adminConsole == null) {
				throw new IllegalStateException();
			}

			try {
				adminConsole.setRemoteUserListener(null);
				onDisconnectedFromServer();
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 
	 */
	@Override
	public void applyConnection(String password) throws ConsoleException {

		//
		verifyServerState();

		if (serverOnline && !connected) {

			try {
				adminConsole = gateway.createAdminsConsole(password);
				adminConsole.setRemoteUserListener(userHandlerStub);
				gui.setAdminConsole(adminConsole);
				onConnectedToServer();
			} catch (RemoteException e) {
				adminConsole = null;
				e.printStackTrace();
			}
		}
	}

	/**
	 * 
	 * @author doctorpro
	 * 
	 */
	private class CloseHandler extends WindowAdapter {

		@Override
		public void windowClosing(WindowEvent e) {

			System.out.println("Start exiting ...");
			if (connected) {
				// release the listener
				;
			}
			System.out.println("Exited");
			System.exit(0);
		}
	}

	/**
	 * 
	 * @author doctorpro
	 * 
	 */
	private class UsersListModel implements ListModel<RemoteUser>, Filterable {

		private String currentFilter = "";

		private List<ListDataListener> l = new ArrayList<ListDataListener>();

		private ArrayList<RemoteUser> allUsers = new ArrayList<RemoteUser>();
		private ArrayList<RemoteUser> filteredUsers = new ArrayList<RemoteUser>();

		/**
		 * 
		 * @param newUsers
		 */
		public void reloadModel(List<RemoteUserImpl> newUsers) {
			allUsers = new ArrayList<RemoteUser>(newUsers);
			setFilter(currentFilter);
		}

		/**
		 * 
		 */
		public void clear() {
			allUsers.clear();
			setFilter(currentFilter);
		}

		@Override
		public void addListDataListener(ListDataListener arg0) {
			l.add(arg0);
		}

		@Override
		public int getSize() {
			return filteredUsers.size();
		}

		@Override
		public void removeListDataListener(ListDataListener arg0) {
			l.remove(arg0);
		}

		/**
		 * 
		 * @param login
		 * @param logged
		 */
		public void setLogged(String login, boolean logged) {

			boolean f = false;

			Iterator<RemoteUser> i = allUsers.iterator();
			while (!f && i.hasNext()) {
				RemoteUser u = i.next();
				if (u.getLogin().equals(login)) {
					((RemoteUserImpl) u).loggedIn = logged;
					f = true;
				}
			}

			if (!f) {
				// throw new IllegalStateException();
			} else {
				fireChanges();
			}
		}

		/**
		 * 
		 * @param u
		 */
		public void removeUser(String login) {

			boolean f = false;

			RemoteUser u;
			Iterator<RemoteUser> i = allUsers.iterator();
			while (!f && i.hasNext()) {
				u = i.next();
				if (u.getLogin().equals(login)) {
					f = true;
					allUsers.remove(u);
				}
			}

			if (f) {
				setFilter(currentFilter);
			}
		}

		/**
		 * 
		 * @param u
		 */
		public void addUser(String login) {
			RemoteUserImpl u = new RemoteUserImpl(login);
			allUsers.add(u);
			setFilter(currentFilter);
		}

		/**
		 * 
		 * @param s
		 */
		@Override
		public void setFilter(String s) {
			currentFilter = s;

			filteredUsers.clear();
			for (Iterator<RemoteUser> i = allUsers.iterator(); i.hasNext();) {
				RemoteUser currentUser = i.next();
				if (currentUser.getLogin().contains(s)) {
					filteredUsers.add(currentUser);
				}
			}
			fireChanges();
		}

		/**
		 * 
		 */
		public void fireChanges() {

			ListDataEvent e = new ListDataEvent(this,
					ListDataEvent.CONTENTS_CHANGED, 0, filteredUsers.size() - 1);

			for (Iterator<ListDataListener> i = l.iterator(); i.hasNext();) {
				i.next().contentsChanged(e);
			}
		}

		@Override
		public RemoteUser getElementAt(int index) {
			return filteredUsers.get(index);
		}
	}

	/**
	 * 
	 * @author doctorpro
	 * 
	 */
	private class RemoteUserHandler implements RemoteUserListener {

		private UsersListModel m;

		/**
		 * 
		 * @param m
		 */
		public RemoteUserHandler(UsersListModel m) {
			this.m = m;
		}

		@Override
		public void userLoggedIn(String userLogin) throws RemoteException {
			m.setLogged(userLogin, true);
		}

		@Override
		public void userLoggedOut(String userLogin) throws RemoteException {
			m.setLogged(userLogin, false);
		}

		@Override
		public void userAction(String login, String methodName, String marker,
				Map<String, String> userArguments) throws RemoteException {
			System.out.println("User action");
		}

		@Override
		public void userRemoved(String userLogin) throws RemoteException {
			m.removeUser(userLogin);
		}

		@Override
		public void userAdded(String userLogin) throws RemoteException {
			m.addUser(userLogin);
		}
	}

	/**
	 * 
	 * @author doctorpro
	 * 
	 */
	private class RemoteUserImpl implements RemoteUser {

		private boolean loggedIn = false;
		private String login;
		private String loggedInString;
		private String notLoggedInString;

		public RemoteUserImpl(String login) {
			this.login = login;

			if (login == null) {
				throw new NullPointerException("Login cannot be null");
			}

			loggedInString = "[On]" + login;
			notLoggedInString = login;
		}

		@Override
		public boolean isLoggedIn() {
			return loggedIn;
		}

		@Override
		public String getLogin() {
			return login;
		}

		@Override
		public String toString() {
			if (loggedIn) {
				return loggedInString;
			} else {
				return notLoggedInString;
			}
		}

	}

	@Override
	public void shutdown() {
		try {
			if (connected) {
				adminConsole.shutdownServer();
			}
		} catch (RemoteException e) {
			e.printStackTrace();
		}
	}
}

interface RemoteUser {

	public boolean isLoggedIn();

	public String getLogin();

	@Override
	public String toString();
}
